/* Zatok - язык программирования текстовых игр
 * (С) 2010 - 2012 Меркулов Матвей Юрьевич
 * Код лицензирован под лицензией GNU LGPL
 * Вы можете прочитать текст лицензии по адресу:
 * http://www.gnu.org/copyleft/lesser.html
 * или прочитать русский перевод по адресу:
 * http://ru.wikisource.org/wiki/GNU_Lesser_General_Public_License
 */

package base.cycle;

import base.CObject;
import base.CProject;
import base.module.CModule;
import base.value.CValue;
import base.value.userobject.CUserObject;
import java.util.ConcurrentModificationException;
import java.util.LinkedList;
import java.util.ListIterator;
import parser.CParser;

public class CForEach extends CModule {
	private LinkedList<CModule> Modules = new LinkedList<CModule>();
	private CObject Iterator;
	private CObject Collection;
	
	private enum ESection { Iterator, Collection };
	
	

	@Override
	public CObject Parse( LinkedList<String> Tokens ) {
		SetFilePosition();
		
		ESection Section = ESection.Iterator;
		LinkedList<String> IteratorTokens = new LinkedList<String>();
		LinkedList<String> CollectionTokens = new LinkedList<String>();
		for ( String Token : Tokens ) {
			if ( Token.equals( "циклдлякаждого" ) ) continue;
			if ( Token.equals( "из") ) {
				Section = ESection.Collection;
			} else {
				switch( Section ) {
					case Iterator:
						IteratorTokens.add( Token );
						break;
					case Collection:
						CollectionTokens.add( Token );
						break;
				}
			}
		}

		Iterator = CParser.ParseFunction( IteratorTokens );
		Collection = CParser.ParseFunction( CollectionTokens );

		ParseModules( Modules, "цикла", "КонецЦикла" );
		return this;
	}



	@Override
	public void Execute() {
		CValue CollectionValue = Collection.GetValue();
		switch( CollectionValue.GetType() ) {
			case UserObject:
				CUserObject Object = CollectionValue.ToObject();
				ListIterator<CUserObject> OldIterator = CProject.CurrentObjectIterator;
				CUserObject OldIteratingObject = CProject.CurrentIteratingObject;
				
				for( CProject.CurrentObjectIterator = Object.Children.listIterator(); CProject.CurrentObjectIterator.hasNext(); ) {
					CProject.CurrentIteratingObject = CProject.CurrentObjectIterator.next();
					Iterator.EquateTo( CProject.CurrentIteratingObject );
					try {
						if( ExecuteModules() ) break;
					} catch( ConcurrentModificationException Exception ) {
						Error( "Изменение содержимого объекта во время цикла по его элементам" );
					}
				}
				
				CProject.CurrentObjectIterator = OldIterator;
				CProject.CurrentIteratingObject = OldIteratingObject;
				break;
			case Array:
				CValue[] Array = CollectionValue.ToArray();
				for( int N = 0; N < Array.length; N++ ) {
					Iterator.EquateTo( Array[ N ] );
					if( ExecuteModules() ) break;
				}
				break;
			case List:
				ListIterator<CValue> OldListIterator = CProject.CurrentListIterator;
				ListIterator<CUserObject> OldObjectIterator = CProject.CurrentObjectIterator;
				CProject.CurrentObjectIterator = null;
				
				LinkedList<CValue> List = CollectionValue.ToList();
				for( CProject.CurrentListIterator = List.listIterator(); CProject.CurrentListIterator.hasNext(); ) {
					Iterator.EquateTo( CProject.CurrentListIterator.next() );
					try {
						if( ExecuteModules() ) break;
					} catch( ConcurrentModificationException Exception ) {
						Error( "Изменение списка во время цикла по его элементам" );
					}
				}
				
				CProject.CurrentObjectIterator = OldObjectIterator;
				CProject.CurrentListIterator = OldListIterator;
				break;
		}
	}

	
	
	private boolean ExecuteModules() {
		ExecuteModulesList( Modules );
		if ( CProject.ExitStatus == CProject.EExitStatus.Break ) {
			CProject.ExitStatus = CProject.EExitStatus.No;
			return true;
		} else if ( CProject.ExitStatus == CProject.EExitStatus.ExitFunction ) {
			return true;
		}
		CProject.ExitStatus = CProject.EExitStatus.No;
		return false;
	}
}
